<?php
/**
 * @package mikrokosmos
 * mk classes structures file created on 20-apr-2005
 * jaco_at_pixeldump_dot_org
 */

/**
 * tag header class
 */
class MKTagHeader {

	var $tagBuf;
	var $tagID;
	var $tagType;
	var $itemID  = 0;
	var $tagSL; // short or long tag
	var $tagLength;
	var $offset = -1;			//offset start from "swf body" data

	function MKTagHeader($tagBuf, $offset = -1){

		if(strlen($tagBuf)){
			$this->tagBuf = $tagBuf;
			$this->get_tag_header();
		}

		$this->offset = $offset;
	}
	//
	function set_tagBuf($tagBuf) { $this->tagBuf = $tagBuf; }
	function get_tagBuf() { return $this->tagBuf; }

	//
	function set_tagID($tagID) { $this->tagID = $tagID; }
	function get_tagID() { return $this->tagID; }

	//
	function set_tagType($tagType) { $this->tagType = $tagType; }
	function get_tagType() { return $this->tagType; }

	//
	function set_itemID($itemID) { $this->itemID = $itemID; }
	function get_itemID() { return $this->itemID; }

	//
	function set_tagSL($tagSL) { $this->tagSL = $tagSL; }
	function get_tagSL() { return $this->tagSL; }

	//
	function set_tagLength($tagLength) { $this->tagLength = $tagLength; }
	function get_tagLength() { return $this->tagLength; }

	//
	function set_offset($offset) { $this->offset = $offset; }
	function get_offset() { return $this->offset; }

	//
	function set_tag_header($tagHData = array()){

		if(!count($tagHData)){
			$tagHData = array();
			$tagHData = $this->fetch_assoc();
		}

		$this->tagBuf = write_tag_header_data($tagHData);

		return $this->tagBuf;
	}

	function get_tag_header($tagHData = array()){

		if(!count($tagHData)){
			$tagHData = array();
			$tagHData = read_tag_header_data(substr($this->tagBuf, 0, 8));
		}

		$this->tagID = $tagHData["tagID"];
		$this->itemID = $tagHData["itemID"];
		$this->tagType = $tagHData["tagType"];
		$this->tagSL = $tagHData["tagSL"];
		$this->tagLength = $tagHData["tagLength"];
		$this->tagBuf = substr($this->tagBuf, 0, $this->tagSL + 2); // strip unused bytes, keep itemID
	}

	// returns an associative array width class members
	// as keys
	function fetch_assoc(){

		$tagHData = array();
		$tagHData["tagID"] = $this->tagID;
		$tagHData["itemID"] = $this->itemID;
		$tagHData["tagSL"] = $this->tagSL;
		$tagHData["tagLength"] = $this->tagLength;
		$tagHData["offset"] = $this->offset;

		return $tagHData;
	}

	//
	function fetch_xml($includeHeader = true){

		$xmlStr = $includeHeader ? "<? xml version=\"1.0\"?>" : "";
		$th = $this->fetch_assoc();
		$xmlStr .= "\r\n\t<tagHeader ";

		foreach($th as $k => $v){

			if($k == "offset" && $v == -1) continue;
			$xmlStr .= $k ."=\"" .$v ."\"";
		}

		return $xmlStr . " />";
	}


	function get_length($hasID = false){
		$length = 2;
		if($this->tagLength >= 63) $length += 4;
		if($hasID) $length += 2;

		return $length;
	}
}

/**
 * CXFORM
 */
class MKCxform {

	var $hasAddTerms;		// UB[1] Has color addition values if equal to 1
	var $hasMultTerms;		// UB[1] Has color multiply values if equal to 1
	var $nBits;				// UB[4] Bits in each value field
	var $redMultTerm;		// If HasMultTerms = 1, SB[Nbits] Red multiply value
	var $greenMultTerm;		// If HasMultTerms = 1, SB[Nbits] Green multiply value
	var $blueMultTerm;		// If HasMultTerms = 1, SB[Nbits] Blue multiply value
	var $redAddTerm;		// If HasAddTerms = 1, SB[Nbits] Red addition value
	var $greenAddTerm;		// If HasAddTerms = 1, SB[Nbits] Green addition value
	var $blueAddTerm;		// If HasAddTerms = 1, SB[Nbits] Blue addition value

	var $buf;

	// the constructor
	function MKCxform ($buf = ""){

		if(strlen($buf))
			$this->buf = $buf;
	}


	function set_hasAddTerms($hasAddTerms) { $this->hasAddTerms = $hasAddTerms; }
	function get_hasAddTerms() { return $this->hasAddTerms; }

	function set_hasMultTerms($hasMultTerms) { $this->hasMultTerms = $hasMultTerms; }
	function get_hasMultTerms() { return $this->hasMultTerms; }

	function set_nBits($nBits) { $this->nBits = $nBits; }
	function get_nBits() { return $this->nBits; }

	function set_redMultTerm($redMultTerm) { $this->redMultTerm = $redMultTerm; }
	function get_redMultTerm() { return $this->redMultTerm; }

	function set_greenMultTerm($greenMultTerm) { $this->greenMultTerm = $greenMultTerm; }
	function get_greenMultTerm() { return $this->greenMultTerm; }

	function set_blueMultTerm($blueMultTerm) { $this->blueMultTerm = $blueMultTerm; }
	function get_blueMultTerm() { return $this->blueMultTerm; }

	function set_redAddTerm($redAddTerm) { $this->redAddTerm = $redAddTerm; }
	function get_redAddTerm() { return $this->redAddTerm; }

	function set_greenAddTerm($greenAddTerm) { $this->greenAddTerm = $greenAddTerm; }
	function get_greenAddTerm() { return $this->greenAddTerm; }

	function set_blueAddTerm($blueAddTerm) { $this->blueAddTerm = $blueAddTerm; }
	function get_blueAddTerm() { return $this->blueAddTerm; }

	function set_buf($buf) { $this->buf = $buf; }
	function get_buf() { return $this->buf; }

	//
	function set_cxform_data($cfd = array()){

		if(!count($cfd)){
			$cfd = array();
			$cfd = $this->fetch_assoc();
		}

		$this->buf = write_cxform_data($cfd);

		return $this->buf;
	}

	//
	function get_cxform_data($cfd = array()){

		if(!count($cfd)){
			$cfd = array();
			$cfd = read_cxform_data($this->buf);
		}

		$this->hasAddTerms = $cfd["hasAddTerms"];
		$this->hasMultTerms = $cfd["hasMultTerms"];
		$this->nBits = $cfd["nBits"];
		$this->redMultTerm = $cfd["redMultTerm"];
		$this->greenMultTerm = $cfd["greenMultTerm"];
		$this->blueMultTerm = $cfd["blueMultTerm"];
		$this->redAddTerm = $cfd["redAddTerm"];
		$this->greenAddTerm = $cfd["greenAddTerm"];
		$this->blueAddTerm = $cfd["blueAddTerm"];
	}

	//
	function fetch_assoc(){

		$cfd = array();

		$cfd["hasAddTerms"]		= $this->hasAddTerms;
		$cfd["hasMultTerms"]	= $this->hasMultTerms;
		$cfd["nBits"]			= $this->nBits;
		$cfd["redMultTerm"]		= $this->redMultTerm;
		$cfd["greenMultTerm"]	= $this->greenMultTerm;
		$cfd["blueMultTerm"]	= $this->blueMultTerm;
		$cfd["redAddTerm"]		= $this->redAddTerm;
		$cfd["greenAddTerm"]	= $this->greenAddTerm;
		$cfd["blueAddTerm"]		= $this->blueAddTerm;

		return $cfd;
	}

	//
	function update_cxform(){
	}

	//
	function update_buf(){ return $this->set_cxform_data();	}
}

/**
 * CXFORMWITHALPHA
 */
class MKCxformWithAlpha {

	var $hasAddTerms = 0;	// UB[1] Has color addition values if equal to 1
	var $hasMultTerms = 0;	//  UB[1] Has color multiply values if equal to 1
	var $nBits = 0;			//  UB[4] Bits in each value field
	var $redMultTerm = 0;	//  If HasMultTerms = 1, SB[Nbits] Red multiply value
	var $greenMultTerm = 0;	//  If HasMultTerms = 1, SB[Nbits] Green multiply value
	var $blueMultTerm = 0;	//  If HasMultTerms = 1, SB[Nbits] Blue multiply value
	var $alphaMultTerm = 0;	//  If HasMultTerms = 1, SB[Nbits] Alpha multiply value
	var $redAddTerm = 0;	//  If HasAddTerms = 1, SB[Nbits] Red addition value
	var $greenAddTerm = 0;	//  If HasAddTerms = 1, SB[Nbits] Green addition value
	var $blueAddTerm = 0;	//  If HasAddTerms = 1, SB[Nbits] Blue addition value
	var $alphaAddTerm = 0;	//  If HasAddTerms = 1, SB[Nbits] Transparency addition value

	var $buf;

	// the constructor
	function MKCxformWithAlpha ($buf = "", $cwad = array()){

		if(strlen($buf))
			$this->buf = $buf;

		if(strlen($buf) || count($cwad))
			$this->get_cxform_with_alpha_data($cwad);
	}

	function set_hasAddTerms($hasAddTerms) { $this->hasAddTerms; $hasAddTerms; }
	function get_hasAddTerms() { return $this->hasAddTerms; }

	function set_hasMultTerms($hasMultTerms) { $this->hasMultTerms;$hasMultTerms; }
	function get_hasMultTerms() { return $this->hasMultTerms; }

	function set_nBits($nBits) { $this->nBits;$nBits; }
	function get_nBits() { return $this->nBits; }

	function set_redMultTerm($redMultTerm) { $this->redMultTerm;$redMultTerm; }
	function get_redMultTerm() { return $this->redMultTerm; }

	function set_greenMultTerm($greenMultTerm) { $this->greenMultTerm;$greenMultTerm; }
	function get_greenMultTerm() { return $this->greenMultTerm; }

	function set_blueMultTerm($blueMultTerm) { $this->blueMultTerm;$blueMultTerm; }
	function get_blueMultTerm() { return $this->blueMultTerm; }

	function set_alphaMultTerm($alphaMultTerm) { $this->alphaMultTerm;$alphaMultTerm; }
	function get_alphaMultTerm() { return $this->alphaMultTerm; }

	function set_redAddTerm($redAddTerm) { $this->redAddTerm;$redAddTerm; }
	function get_redAddTerm() { return $this->redAddTerm; }

	function set_greenAddTerm($greenAddTerm) { $this->greenAddTerm;$greenAddTerm; }
	function get_greenAddTerm() { return $this->greenAddTerm; }

	function set_blueAddTerm($blueAddTerm) { $this->blueAddTerm;$blueAddTerm; }
	function get_blueAddTerm() { return $this->blueAddTerm; }

	function set_alphaAddTerm($alphaAddTerm) { $this->alphaAddTerm;$alphaAddTerm; }
	function get_alphaAddTerm() { return $this->alphaAddTerm; }

	function set_buf($buf) { $this->buf = $buf; }
	function get_buf() { return $this->buf; }

	//
	function set_cxform_with_alpha_data($cwad = array()){

		if(!count($cwad)){
			$cwad = array();
			$cwad = $this->fetch_assoc();
		}

		$this->buf = write_cxform_with_alpha_data($cwad);

		return $this->buf;
	}

	//
	function get_cxform_with_alpha_data($cwad = array()){

		if(!count($cwad)){
			$cwad = array();
			$cwad = read_cxform_with_alpha_data($this->buf);
		}

		$this->hasAddTerms		= $cwad["hasAddTerms"];
		$this->hasMultTerms		= $cwad["hasMultTerms"];
		$this->nBits			= $cwad["nBits"];
		$this->redMultTerm		= $cwad["redMultTerm"];
		$this->greenMultTerm	= $cwad["greenMultTerm"];
		$this->blueMultTerm		= $cwad["blueMultTerm"];
		$this->alphaMultTerm	= $cwad["alphaMultTerm"];
		$this->redAddTerm		= $cwad["redAddTerm"];
		$this->greenAddTerm		= $cwad["greenAddTerm"];
		$this->blueAddTerm		= $cwad["blueAddTerm"];
		$this->alphaAddTerm		= $cwad["alphaAddTerm"];
	}

	//
	function fetch_assoc(){

		$cwad = array();

		$cwad["hasAddTerms"]	= $this->hasAddTerms;
		$cwad["hasMultTerms"]	= $this->hasMultTerms;
		$cwad["nBits"]			= $this->nBits;
		$cwad["redMultTerm"]	= $this->redMultTerm;
		$cwad["greenMultTerm"]	= $this->greenMultTerm;
		$cwad["blueMultTerm"]	= $this->blueMultTerm;
		$cwad["alphaMultTerm"]	= $this->alphaMultTerm;
		$cwad["redAddTerm"]		= $this->redAddTerm;
		$cwad["greenAddTerm"]	= $this->greenAddTerm;
		$cwad["blueAddTerm"]	= $this->blueAddTerm;
		$cwad["alphaAddTerm"]	= $this->alphaAddTerm;

		return $cwad;
	}

	function update_buf(){ return $this->set_cxform_with_alpha_data(); }
}


/**
 * RECT structure
 * with read/write methods
 */
class MKRect {

	var $buf = "";
	var $nBits = 0;
	var $xmin = 0;
	var $xmax = 0;
	var $ymin = 0;
	var $ymax = 0;
	var $pad = 0;
	var $rectLength = 0;

	// the constructor
	function MKRect($buf = "", $xmin = 0, $xmax = 0, $ymin = 0, $ymax = 0){

		$this->buf = $buf;

		if(strlen($buf)) {
			$this->buf = $buf;
			$this->get_rect_data();
			return;
		}

		$this->xmin = $xmin;
		$this->xmax = $xmax;
		$this->ymin = $ymin;
		$this->ymax = $ymax;
	}

	function set_buf($buf) { $this->buf = $buf; }
	function get_buf() { return $this->buf; }

	function set_nBits($nBits) { $this->nBits = $nBits; }
	function get_nBits() { return $this->nBits; }

	function set_xmin($xmin) { $this->xmin = $xmin; }
	function get_xmin($inTwips = true) { return $inTwips ? $this->xmin : twip2nmb($this->xmin); }

	function set_xmax($xmax) { $this->xmax = $xmax; }
	function get_xmax($inTwips = true) { return $inTwips ? $this->xmax : twip2nmb($this->xmax); }

	function set_ymin($ymin) { $this->ymin = $ymin; }
	function get_ymin($inTwips = true) { return $inTwips ? $this->ymin : twip2nmb($this->ymin); }

	function set_ymax($ymax) { $this->ymax = $ymax; }
	function get_ymax($inTwips = true) { return $inTwips ? $this->ymax : twip2nmb($this->ymax); }

	function set_pad($pad) { $this->pad = $pad; }
	function get_pad() { return $this->pad; }

	//
	function set_rect_data($rd = array()){

		$this->buf = write_rect_data($this->xmin,
									$this->xmax,
									$this->ymin,
									$this->ymax);

		return $this->buf;
	}

	//
	function get_rect_data($rd = array()){

		if(!count($rd)) $rd = read_rect_data($this->buf);

		$this->nBits = $rd["nBits"];
		$this->xmin = $rd["xmin"];
		$this->xmax = $rd["xmax"];
		$this->ymin = $rd["ymin"];
		$this->ymax = $rd["ymax"];
		$this->rectLength = $rd["rectLength"];

		if(isset($rd["pad"])) $this->pad = $rd["pad"];

		$this->buf = substr($this->buf, 0, $rd["rectLength"]);
	}

	// returns an associative array from class members
	function fetch_assoc($inTwips = true){

		$rd = array();

		if(!$this->nBits){
			$this->nBits = find_minBits(array($this->xmin, $this->xmax, $this->ymin, $this->ymax));
			$this->pad = get_pad($this->nBits * 4 + 5);
		}

		$rd["nBits"] = $this->nBits;

		if($inTwips){
			$rd["xmin"] = $this->xmin;
			$rd["xmax"] = $this->xmax;
			$rd["ymin"] = $this->ymin;
			$rd["ymax"] = $this->ymax;
		}
		else{
			$rd["xmin"] = twip2nmb($this->xmin);
			$rd["xmax"] = twip2nmb($this->xmax);
			$rd["ymin"] = twip2nmb($this->ymin);
			$rd["ymax"] = twip2nmb($this->ymax);
		}
		$rd["pad"] = $this->pad;
		$rd["rectLength"] = $this->rectLength;

		return $rd;
	}

	//
	function fetch_xml($includeHeader = true, $tabLevel = 0, $nodeName = "rect"){
		$xmlStr = $includeHeader ? XML_HEADER : "";
		$pfx = get_xml_indent_row(++$tabLevel);

		$xmlStr .= $pfx ."<" .$nodeName ." ";
		$xmlStr .= "xmin=\"" .twip2nmb($this->xmin) ."px\" ";
		$xmlStr .= "xmax=\"" .twip2nmb($this->xmax) ."px\" ";
		$xmlStr .= "ymin=\"" .twip2nmb($this->ymin) ."px\" ";
		$xmlStr .= "ymax=\"" .twip2nmb($this->ymax) ."px\" />";

		return $xmlStr;
	}


	// refresh and returns buf data from its members
	// alias of set_rect_data
	function update_buf(){ return $this->set_rect_data(); }

	function get_length() {

		$length = (5 + $this->nBits * 4 + $this->pad) / 8;
		$this->buf = substr($this->buf, 0, $length);	// strip exceeding bytes
		return $length;
	}
}

///////////////////////////////////////////////////////////////////////////////
// STRUCTURES
// they are ALL byte-aligned
///////////////////////////////////////////////////////////////////////////////


/**
 * COLOR
 */
class MKColor {

	var $tagID;
 	var $red;
 	var $green;
 	var $blue;
 	var $alpha;

 	function MKColor($buf = "", $tagID = SHAPE_ID){

 		$this->tagID = $tagID;

 		if(strlen($buf)){
 			$this->buf = $buf;
 			$this->get_color_data();
 		}
 	}

 	function set_tagID($tagID){ $this->tagID = $tagID; }
 	function get_tagID(){ return $this->tagID; }

 	function set_red($red){ $this->red = $red; }
 	function get_red(){ return $this->red; }

 	function set_green($green){ $this->green = $green; }
 	function get_green(){ return $this->green; }

 	function set_blue($blue){ $this->blue = $blue; }
 	function get_blue(){ return $this->blue; }

 	function set_alpha($alpha){
		$this->tagID = SHAPE_ID3;
 		$this->alpha = $alpha;
	}
 	function get_alpha(){ return $this->alpha; }

 	function set_color($cAr){

 		$this->red = $cAr["red"];
 		$this->green = $cAr["green"];
 		$this->blue = $cAr["blue"];

 		if(isset($cAr["alpha"])){

 			$this->tagID = SHAPE_ID3;
 			if(isset($cAr["alpha"])) $this->alpha = $cAr["alpha"];
 		}
 		else if($this->tagID == SHAPE_ID3) $this->alpha = 0xff;
 	}

 	// this is an alias of fetch_assoc (easier to remember)
 	function get_color(){
 		$cAr = $this->fetch_assoc();
 		return $cAr;
 	}

 	function set_color_data($cAr = array()){

 		if(count($cAr)){
 			$this->get_color_data($cAr);
 		}
 		else {
 			$cAr = array();
 			$cAr = $this->fetch_assoc();
 		}

		$this->buf = write_color_data($cAr);

		return $this->buf;
 	}

 	function get_color_data($cAr = array()){

 		if(!count($cAr)){
	 		$cAr = array();
 			$cAr = read_color_data($this->buf, $this->tagID);
 		}

 		$this->red = $cAr["red"];
 		$this->green = $cAr["green"];
 		$this->blue = $cAr["blue"];

 		if($this->tagID == SHAPE_ID3)
 			$this->alpha = $cAr["alpha"];
 	}

 	//
 	function update_tagID($tagID){

		if($this->tagID == $tagID) return;

 		$this->tagID = $tagID;

 		if($tagID == SHAPE_ID3){
 			if(!isset($this->alpa)) $this->alpha = 0xff;
 			return;
 		}

		unset($this->alpha);
 	}

 	// returns an associative array from class members
 	function fetch_assoc(){

 		$cAr = array();
 		$cAr["red"] = $this->red;
 		$cAr["green"] = $this->green;
 		$cAr["blue"] = $this->blue;
 		//$cAr["alpha"] = $this->alpha;

 		if($this->tagID == SHAPE_ID3) $cAr["alpha"] = $this->alpha;

 		return $cAr;
 	}

	//
	function fetch_xml($includeHeader = true, $tabLevel = 0, $nodeName = "color"){

		$xmlStr = $includeHeader ? XML_HEADER : "";
		$pfx = get_xml_indent_row(++$tabLevel);

		$xmlStr .= $pfx ."<" .$nodeName ." ";
		$xmlStr .= "red=\"" .$this->red ."\" ";
		$xmlStr .= "green=\"" .$this->green ."\" ";
		$xmlStr .= "blue=\"" .$this->blue ."\" ";

		if($this->tagID == SHAPE_ID3);
			$xmlStr .= "alpha=\"" .$this->alpha ."\"";

		$xmlStr .=  " />";

		return $xmlStr;
	}


 	// update buf data from its members
 	// this an alias of set_color_data
 	function update_buf(){ return $this->set_color_data(); }

 	// returns the COLOR byte length
 	function get_length(){	return ($this->tagID == SHAPE_ID3) ? 4 : 3; }
}


/**
 * MATRIX record
 *
 */
class MKMatrix {

	var $buf;					// raw binData
	var $hasScale		= 0;	// UB[1] 1 = Has scale values
	var $nScaleBits		= 0;	// if(hasScale), UB[5] nScaleBits
	var $scaleX			= 1;	// if(hasScale), FB[nScaleBits] x scale value 0 < 1 fp 16.16
	var $scaleY			= 1;	// if(hasScale), FB[nScaleBits] y scale value 0 < 1 fp 16.16
	var $hasRotate		= 0;	// UB[1] 1 = Has rotate and skew values
	var $nRotateBits	= 0;	// if(hasRotate), UB[5] nRotateBits
	var $rotateSkew0	= 0;	// if(hasRotate), FB[nRotateBits] First rotate and skew value fp 16.16
	var $rotateSkew1	= 0;	// if(hasRotate), FB[nRotateBits] Second rotate and skew value fp 16.16
	var $nTranslateBits = 0;	// UB[5] nTranslateBits, this field is always present
	var $translateX		= 0;	// SB[nTranslateBits] x translate value in twips
	var $translateY		= 0;	// SB[nTranslateBits] y translate value in twips

	var $sx			= 1;		// real value of scale x
	var $sy 		= 1;		// real value of scale y
	var $tx 		= 0;		// real value of x translation
	var $ty 		= 0;		// real value of y translation
	var $rotation	= 0;		// real value of rotation in degrees


	var $imageMatrixFlag = false;	// this flag is needed when matrix is used for images

	// the constructor
	function MKMatrix($buf = "", $imageMatrixFlag = false){

		$this->imageMatrixFlag = $imageMatrixFlag;

		if(strlen($buf)){
			$this->buf = $buf;
			$this->get_matrix_data();
		}

		if($imageMatrixFlag) $this->set_scale();
	}

	function get_buf() { return $this->buf; } //
	function get_hasScale() { return $this->hasScale; } // boolean
	function get_nScaleBits() { return $this->nScaleBits; } // integer
	function get_scaleX() { return $this->scaleX; } // fp 16.16
	function get_scaleY() { return $this->scaleY; } // fp 16.16
	function get_hasRotate() { return $this->hasRotate; } // boolean
	function get_nRotateBits() { return $this->nRotateBits; } // integer
	function get_rotateSkew0() { return $this->rotateSkew0; } // fp 16.16
	function get_rotateSkew1() { return $this->rotateSkew1; } // fp 16.16
	function get_nTranslateBits() { return $this->nTranslateBits; } // integer
	function get_translateX() { return $this->translateX; } // signed twips
	function get_translateY() { return $this->translateY; } // signed twips

	function set_imageMatrixFlag($imageMatrixFlag) { $this->imageMatrixFlag = $imageMatrixFlag; }
	function get_imageMatrixFlag() { return $this->imageMatrixFlag; }

	//
	function set_position($x = 0, $y = 0){
		$this->tx = nmb2twip($x);
		$this->ty = nmb2twip($y);
	}
	function get_position($xy = "x"){
		return ($xy == "x") ? $this->tx : $this->ty;
	}

	//
	function set_positionX($x = 0) {
		$this->tx = nmb2twip($x);
	}
	function set_positionY($y = 0) {
		$this->ty = nmb2twip($y);
	}

	//
	function set_scale($sx = 100, $sy = 100, $forceScale = false){

		$this->sx = $sx / 100;
		$this->sy = $sy / 100;
		$this->hasScale = ($this->sx != 1 && $this->sy != 1) ? 1 : 0;
		if($forceScale){
			$this->hasScale = 1;
			$md = $this->matrix_resolve();
			$this->set_matrix_data($md);
		}
	}
	function get_scale($xy = "x"){
		return ($xy == "x") ? $this->sx * 100 : $this->sy * 100;
	}

	// rotation is intended in degree
	function set_rotation($rotation = 0){
		if($rotation > 360) $rotation %= 360;
		if($rotation > 180) $rotation -= 360;
		$this->rotation = $rotation;
		$this->hasRotate = $rotation ? 1 : 0;
	}
	function get_rotation(){ return $this->rotation; }

	// alias of set[get]_position
	function set_translate($tx = 0, $ty = 0){ $this->set_position($tx, $ty); }
	function get_translate($xy = "x"){ return get_position($xy); }

	//
	function matrix_resolve($updateBuf = false){

		// scale A
		$sMatrix = array("a" => $this->sx,
						 "b" => 0,
						 "c" => 0,
						 "d" => $this->sy,
						 "e" => 0,
						 "f" => 0);

		// rotation B
		$cosAngle = 1;
		$sinAngle = 0;

		if($this->hasRotate){

			$radiants = RADIANT * $this->rotation;
			$cosAngle = round(cos($radiants), 2);
			$sinAngle = round(sin($radiants), 2);
		}

		$rMatrix = array("a" => $cosAngle,
						 "b" => $sinAngle,
						 "c" => -$sinAngle,
						 "d" => $cosAngle,
						 "e" => 0,
						 "f" => 0);

		// translate C
		$tMatrix = array("a" => 1,
						 "b" => 0,
						 "c" => 0,
						 "d" => 1,
						 "e" => $this->tx,
						 "f" => $this->ty);

		$mc = $this->matrix_multiply($sMatrix, $rMatrix);
		$md = $this->matrix_multiply($mc, $tMatrix);

		return $this->build_matrix_from_array($md);
	}

	// matrix multiplication
	function matrix_multiply($ma = array(), $mb = array()){
		$mc = array();

		$mc["a"] = $ma["a"] * $mb["a"] + $ma["b"] * $mb["c"];
		$mc["b"] = $ma["a"] * $mb["b"] + $ma["b"] * $mb["d"];
		$mc["c"] = $ma["c"] * $mb["a"] + $ma["d"] * $mb["c"];
		$mc["d"] = $ma["c"] * $mb["b"] + $ma["d"] * $mb["d"];
		$mc["e"] = $ma["e"] * $mb["a"] + $ma["f"] * $mb["c"] + $mb["e"];
		$mc["f"] = $ma["e"] * $mb["b"] + $ma["f"] * $mb["d"] + $mb["f"];

		return $mc;
	}

	// map a,b,c,d,e,f fields to class members
	function build_matrix_from_array($mAr = array()){

		$md = array();

		$md["hasScale"]  = 0;
		$md["hasRotate"] = 0;

		if($mAr["a"] != 1 || $mAr["d"] != 1) $md["hasScale"] = 1;
		$md["nScaleBits"]	  = fp_find_minBits(array($mAr["a"], $mAr["d"]));
		$md["scaleX"]		  = $mAr["a"];
		$md["scaleY"]		  = $mAr["d"];

		if($mAr["b"] || $mAr["c"]) $md["hasRotate"] = 1;
		$md["nRotateBits"]	  = fp_find_minBits(array($mAr["b"], $mAr["c"]));
		$md["rotateSkew0"]	  = $mAr["b"];
		$md["rotateSkew1"]	  = $mAr["c"];

		$md["nTranslateBits"] = find_minBits(array($mAr["e"], $mAr["f"]));
		$md["translateX"]	  = $mAr["e"];
		$md["translateY"]	  = $mAr["f"];


		//$this->get_matrix_data($md);
		//if($updateBuf) return $this->set_matrix_data($md);
		return $md;
	}

	//
	function get_empty_matrix_buf(){ return NIL; }

	//
	function set_matrix_data($md = array(), $matrixResolve = false){

		if(!count($md)){
			$md = $this->fetch_assoc($matrixResolve);
		}
		else if(isset($md["a"])) $md = $this->build_matrix_from_array($md);

		$this->get_matrix_data($md);
		$this->buf = write_matrix_data($md);

		return $this->buf;
	}

	//
	function get_matrix_data($md = array()){

		if(!count($md))	$md = read_matrix_data($this->buf);

		$this->hasScale		  = $md["hasScale"];
		$this->nScaleBits	  = $md["nScaleBits"];
		$this->scaleX		  = $md["scaleX"];
		$this->scaleY		  = $md["scaleY"];
		$this->hasRotate	  = $md["hasRotate"];
		$this->nRotateBits	  = $md["nRotateBits"];
		$this->rotateSkew0	  = $md["rotateSkew0"];
		$this->rotateSkew1	  = $md["rotateSkew1"];
		$this->nTranslateBits = $md["nTranslateBits"];
		$this->translateX	  = $md["translateX"];
		$this->translateY	  = $md["translateY"];
	}


	// returns an associative array from its members
	function fetch_assoc($matrixResolve = false){

		$md = array();

		if($matrixResolve) $md = $this->matrix_resolve();
		else {
			$md["hasScale"]			= $this->hasScale;
			$md["nScaleBits"]		= $this->nScaleBits;
			$md["scaleX"]			= $this->scaleX;
			$md["scaleY"]			= $this->scaleY;
			$md["hasRotate"]		= $this->hasRotate;
			$md["nRotateBits"]		= $this->nRotateBits;
			$md["rotateSkew0"]		= $this->rotateSkew0;
			$md["rotateSkew1"]		= $this->rotateSkew1;
			$md["nTranslateBits"]	= $this->nTranslateBits;
			$md["translateX"]		= $this->translateX;
			$md["translateY"]		= $this->translateY;

			$md["sx"]				= $this->sx;
			$md["sy"]				= $this->sy;
			$md["tx"]				= $this->tx;
			$md["ty"]				= $this->ty;
			$md["rotation"]			= $this->rotation;
		}
		return $md;
	}

	//
	function fetch_xml($includeHeader = true, $tabLevel = 0, $nodeName = "matrix"){

		$xmlStr = $includeHeader ? "<xml version=\"1.0\"?>" : "";
		$pfx = get_xml_indent_row(++$tabLevel);

		$xmlStr .= $pfx ."<" .$nodeName;
		//$xmlStr .= "hasScale=\"" .($this->hasScale ? "1\" " : "0\" ");
		//$xmlStr .= "hasRotate=\"" .($this->hasRotate ? "1\" " : "0\" ");
		$xmlStr .= ">";
		$pfx = get_xml_indent_row(++$tabLevel);

		if($this->hasScale){
			$xmlStr .= $pfx ."<scale ";
			//$xmlStr .= "nScaleBits=\"" .$this->nScaleBits ."\" ";
			$xmlStr .= "scaleX=\"" .$this->scaleX ."\" ";
			$xmlStr .= "scaleY=\"" .$this->scaleY ."\" />";
		}

		if($this->hasRotate){
			$xmlStr .= $pfx ."<rotation ";
			//$xmlStr .= "nRotateBits=\"" .$this->nRotateBits ."\" ";
			$xmlStr .= "rotateSkew0=\"" .$this->rotateSkew0 ."\" ";
			$xmlStr .= "rotateSkew1=\"" .$this->rotateSkew1 ."\" />";
		}

		$xmlStr .= $pfx ."<translation ";
		//$xmlStr .= "nTranslateBits=\"" .$this->nTranslateBits ."\" ";
		if($this->translateX != 0) $xmlStr .= "x=\"" .twip2nmb($this->translateX) ."px\" ";
		if($this->translateY != 0) $xmlStr .= "y=\"" .twip2nmb($this->translateY) ."px\" ";
		$xmlStr .= "/>";

		$pfx = get_xml_indent_row(--$tabLevel);
		$xmlStr .= $pfx ."</" .$nodeName .">";

		return $xmlStr;
	}

	// update and return buf data from its members
	// this is an alias to set_matrix_data
	function update_buf(){
		return $this->set_matrix_data(true);
	}

	// returns MATRIX byte length
	function get_length(){

		$mbl = 7; // MATRIX bit length initial set to 7 (has... flags + translatebits)

		if($this->hasScale) $mbl += 5 + $this->nScaleBits * 2;
		if($this->hasRotate) $mbl += 5 + $this->nRotateBits * 2;

		$mbl += $this->nTranslateBits * 2;

		return (int) ceil($mbl / 8);
	}
}


/**
 * GRADIENT
 */
class MKGradient {

	var $buf;
	var $gradientType = GRADIENT; // GRADIENT | FOCAL_GRADIENT

	var $spreadMode;			// UB[2] 0 = Pad mode
								// 1 = Reflect mode
								// 2 = Repeat mode
								// 3 = Reserved
	var $interpolationMode;		// UB[2] 0 = Normal RGB mode interpolation
								// 1 = Linear RGB mode interpolation
								// 2 and 3 = Reserved
	var $gradientCount;			// UB[4] 1 to 15
	var $gradientRecords = array();
	var $focalPoint = 0;

	function MKGradient($buf = "", $gradientRecords = array()){

		$grCount = count($gradientRecords);

		if(strlen($buf)){
			$this->buf = $buf;
			$this->get_gradient_data();
		}
		else if($grCount){
			$this->gradientCount = $grCount;
			$this->gradientRecords = $gradientRecords;
		}
	}

	//
	function set_buf($buf) { $this->buf = $buf; }
	function get_buf() { return $this->buf; }

	//
	function set_gradientType ($gradientType) { $this->gradientType = $gradientType; }
	function get_gradientType () { return $this->gradientType; }

	//
	function set_spreadMode($spreadMode) { $this->spreadMode = $spreadMode; }
	function get_spreadMode() { return $this->spreadMode; }

	//
	function set_interpolationMode($interpolationMode) { $this->interpolationMode = $interpolationMode; }
	function get_interpolationMode() { return $this->interpolationMode; }

	//
	function set_gradientCount($gradientCount) { $this->gradientCount = $gradientCount; }
	function get_gradientCount() { return $this->gradientCount; }

	//
	function set_gradientRecords($gradientRecords) { $this->gradientRecords = $gradientRecords; }
	function get_gradientRecords() { return $this->gradientRecords; }

	//
	function set_focalPoint($focalPoint) { $this->focalPoint = $focalPoint; }
	function get_focalPoint() { return $this->focalPoint; }

	//
	function set_colors($colors = array()){

		$cCount = count($colors);

		for($i = 0; $i < $cCount; $i++){
			if($i >= 8) break; // for now supports no more than 255 ratios
							   // I'd like to see a shape with greater number!

			if(!isset($this->gradientRecords[$i])) {
				$this->gradientRecords[$i] = new MKGradientRecord();
			}

			$this->gradientRecords[$i]->set_gradientColor($colors[$i]);
		}

		$this->gradientCount = $cCount;
	}
	//function get_colors(){ return $this->colors; }

	//
	function set_ratios($ratios = array()){

		$rCount = count($ratios);

		for($i = 0; $i < $rCount; $i++){

			if($i >= 8) break; // for now supports no more than 255 ratios
							   // I'd like to see a shape with greater number!

			if(!isset($this->gradientRecords[$i])){
				$this->gradientRecords[$i] = new MKGradientRecord();
			}

			$this->gradientRecords[$i]->set_ratio($ratios[$i]);
		}

		$this->gradientCount = $rCount;
	}
	function get_ratios(){return $this->ratios; }

	function set_gradient_data($grd = array()){

		if(count($grd)){
			$this->get_gradient_data($grd);
		}
		else {

			$grd = array();
			$grd = $this->fetch_assoc();
		}

		$this->buf = write_gradient_data($grd);

		return $this->buf;
	}

	function get_gradient_data($grd = array()){

		if(!count($grd)) {
			$grd = array();
			$grd = read_gradient_data($this->buf, $this->tagID);
		}

		$this->gradientCount = $grd["gradientCount"];
		$this->gradientRecords = array();

		foreach($grd["gradientRecords"] as $gr){
			$grCount = count($this->gradientRecords);
			$this->gradientRecords[$grCount] = new MKGradientRecord();
			$this->gradientRecords[$grCount]->set_ratio($gr["ratio"]);
			$this->gradientRecords[$grCount]->set_gradientColor($gr["gradientColor"]);
		}
	}

	// returns an associative array with keys from class members
	function fetch_assoc(){

		$grd = array();
		$grd["gradientCount"] = $this->gradientCount;
		$grd["gradientRecords"] = array();

		foreach($this->gradientRecords as $gr){

			$grd["gradientRecords"][] = $gr->fetch_assoc();

			//["ratio"] = $grd->get_ratio();
			//$gd["gradientRecords"][$gi]["gradientColor"] = $this->gradientRecords[$i]->get_gradientColor();
		}

		return $grd;
	}

	// returns buf data, refresh from its member
	// alias to set_gradient_data
	function update_buf(){ return $this->set_gradient_data(); }

	// returns GRADIENT byte length
	function get_length(){
		return count($this->gradientsRecords) * $this->gradientRecords[0]->get_length() + 1;
	}
}


/**
 * GRADIENTRECORD
 */
class MKGradientRecord {

	var $buf;

	var $ratio;
	var $gradientColor;

	// the constructor
	function MKGradientRecord($buf = ""){

		if(strlen($buf)){
			$this->buf = $buf;
			get_gradientRecord_data();
		}
	}

	function set_buf($buf) { $this->buf = $buf; }
	function get_buf() { return $this->buf; }

	function set_ratio($ratio) { $this->ratio = $ratio; }
	function get_ratio() { return $this->ratio; }

	function set_gradientColor($gc) {
		$this->gradientColor = new MKColor();
		$this->gradientColor->set_color($gc);
	}
	function get_gradientColor() { return $this->gradientColor; }

	function set_gradientRecord_data($grd = array()){

		if(count($grd)){
			$this->get_gradientRecord_data($grd);
		}
		else {
			$grd = array();
			$grd = $this->fetch_assoc();
		}

		$this->buf = write_gradientRecord_data($grd);

		return $this->buf;
	}

	function get_gradientRecord_data($grd = array()){

		if(!count($grd)){
			$grd = array();
			$grd = read_gradientRecord_data($this->buf);
		}

		$this->ratio = $grd["ratio"];
		$this->gradientColor = new MKColor();
		$this->gradientColor->set_color($grd["gradientColor"]);
	}

	// returns an associative array with class members
	function fetch_assoc(){

		$grd["ratio"] = $this->ratio;
		$grd["gradientColor"] = $this->gradientColor->get_color();

		return $grd;
	}

	// update and returns  buf data from its members
	// alias of set_gradient_data
	function update_buf(){ return $this->set_gradientRecord_data(); }

	// return GRADIENTRECORD byte length
	function get_length(){ return $this->gradientColor->get_length() + 1; }
}



// PLEASE NOTE: these are specific swf8 tags
//              don't know it it make sense to implement
//              (probably not)

/**
 * swf8 and above
 * FILTERLIST
 */
class MKFilterList {
/*
	var $filterCount UI8 Number of Filters
Filter FILTER[NumberOfFilters] List of filters
FILTER
Field Type Comment
FilterID UI8 0 = Has DropShadowFilter
1 = Has BlurFilter
2 = Has GlowFilter
3 = Has BevelFilter
4 = Has
GradientGlowFilter
5 = Has ConvolutionFilter
6 = Has ColorMatrixFilter
7 = Has
GradientBevelFilter
DropShadowFilter If FilterID = 0
DROPSHADOWFILTER
Drop Shadow filter
BlurFilter If FilterID = 1
BLURFILTER
Blur filter
GlowFilter If FilterID = 2
GLOWFILTER
Glow filter
BevelFilter If FilterID = 3
BEVELFILTER
Bevel filter
GradientGlowFilter If FilterID = 4
GRADIENTGLOWFILTER
Gradient Glow filter
ConvolutionFilter If FilterID = 5
CONVOLUTIONFILTER
Convolution filter
ColorMatrixFilter If FilterID = 6
COLORMATRIXFILTER
Color Matrix filter
GradientBevelFilter If FilterID = 7
GRADIENTBEVELFILTER
Gradient Bevel filte
*/
}

/**
 * swf8 and above
 * COLORMATRIXFILTER
 */
class MKColorMatrixFilter {
	/*
	Matrix FLOAT[20] Color matrix values
	*/
}

/**
 * swf8 and above
 * CONVOLUTIONFILTER
 */
class MKConvolutionFilter {
/*
	MatrixX UI8 Horizontal matrix size
MatrixY UI8 Vertical matrix size
Divisor FLOAT Divisor applied to the
matrix values
Bias FLOAT Bias applied to the matrix
values
Matrix FLOAT[MatrixX * MatrixY] Matrix values
DefaultColor RGBA Default color for pixels
outside the image
Reserved UB[6] Must be 0
Clamp UB[1] Clamp mode
PreserveAlpha UB[1] Preserve the alpha
*/
}



/**
 * swf8 and above
 * BLURFILTER
 */
class MKBlurFilter {
/*
	BlurX FIXED Horizontal blur amount
BlurY FIXED Vertical blur amount
Passes UB[5] Number of blur passes
Reserved UB[3] Must be 0
*/
}

/**
 * swf8 and above
 * DROPSHADOWFILTER
 */
class MKDropShadowFilter {
/*
DropShadowColor RGBA Color of the shadow
BlurX FIXED Horizontal blur amount
BlurY FIXED Vertical blur amount
Angle FIXED Radian angle of the drop
shadow
Distance FIXED Distance of the drop
shadow
Display list tags 61
Glow filter
The Glow filter works in the same way as the Drop Shadow filter, except that it does not have
a distance and angle parameter. Therefore, it can run slightly faster.
Strength FIXED8 Strength of the drop
shadow
InnerShadow UB[1] Inner shadow mode
Knockout UB[1] Knockout mode
CompositeSource UB[1] Composite source
Always 1
Passes UB[5] Number of blur passes*/
}

/**
 * swf8 and above
 * GLOWFILTER
 */
class MKGlowFilter {
/*
GlowColor RGBA Color of the shadow
BlurX FIXED Horizontal blur amount
BlurY FIXED Vertical blur amount
Strength FIXED8 Strength of the glow
InnerGlow UB[1] Inner glow mode
Knockout UB[1] Knockout mode
CompositeSource UB[1] Composite source
Always 1
Passes UB[5] Number of blur passes*/
}

/**
 * swf8 and above
 * BEVELFILTER
 */
class MKBevelFilter {
/*
ShadowColor RGBA Color of the shadow
HighlightColor RGBA Color of the highlight
BlurX FIXED Horizontal blur amount
BlurY FIXED Vertical blur amount
Angle FIXED Radian angle of the drop
shadow
Distance FIXED Distance of the drop
shadow
Strength FIXED8 Strength of the drop
shadow
InnerShadow UB[1] Inner shadow mode
Knockout UB[1] Knockout mode
CompositeSource UB[1] Composite source
Always 1
OnTop UB[1] OnTop mode
Passes UB[4] Number of blur passes*/
}

/**
 * swf8 and above
 * GRADIENTGLOWFILTER
 */
class MKGradientGlowFilter {
/*
NumColors UI8 Number of colors in the
gradient
GradientColors RGBA[NumColors] Gradient colors
GradientRatio UI8[NumColors] Gradient ratios
Display list tags 63
BlurX FIXED Horizontal blur amount
BlurY FIXED Vertical blur amount
Angle FIXED Radian angle of the
gradient glow
Distance FIXED Distance of the gradient
glow
Strength FIXED8 Strength of the gradient
glow
InnerShadow UB[1] Inner glow mode
Knockout UB[1] Knockout mode
CompositeSource UB[1] Composite source
Always 1
OnTop UB[1] OnTop mode
Passes UB[4] Number of blur passes*/
}

/**
 * swf8 and above
 * GRADIENTBEVELFILTER
 */
class MKGradientBevelFilter {
/*
NumColors UI8 Number of colors in the
gradient
GradientColors RGBA[NumColors] Gradient colors
GradientRatio UI8[NumColors] Gradient ratios
BlurX FIXED Horizontal blur amount
BlurY FIXED Vertical blur amount
Angle FIXED Radian angle of the
gradient bevel
Distance FIXED Distance of the gradient
bevel
Strength FIXED8 Strength of the gradient
bevel
InnerShadow UB[1] Inner bevel mode
Knockout UB[1] Knockout mode
CompositeSource UB[1] Composite source
Always 1
OnTop UB[1] OnTop mode
Passes UB[4] Number of blur passes*/
}


// ---------------------------------- end of mk structures
?>
